#pragma once
#include <Common/platform.h>
#include <cmath>

namespace shermit {
	inline __host__ __device__ unsigned int leftShift3f_32b(unsigned int x) {
		if (x == (1 << 10)) --x;
		x = (x | (x << 16)) & 0b00000011000000000000000011111111;
		x = (x | (x << 8)) & 0b00000011000000001111000000001111;
		x = (x | (x << 4)) & 0b00000011000011000011000011000011;
		x = (x | (x << 2)) & 0b00001001001001001001001001001001;
		return x;
	}

	inline __host__ __device__ unsigned long long leftShift3f_64b(unsigned long long v) {
		unsigned long long x = v & 0x1fffff;
		x = (x | x << 32) & 0x1f00000000ffff;
		x = (x | x << 16) & 0x1f0000ff0000ff;
		x = (x | x << 8) & 0x100f00f00f00f00f;
		x = (x | x << 4) & 0x10c30c30c30c30c3;
		x = (x | x << 2) & 0x1249249249249249;
		return x;
	}

	inline __host__ __device__ unsigned int morton3f_32b(float x, float y, float z) {
		unsigned int ux = fminf(fmaxf(x * 1024.0f, 0.0f), 1023.0f);
		unsigned int uy = fminf(fmaxf(y * 1024.0f, 0.0f), 1023.0f);
		unsigned int uz = fminf(fmaxf(z * 1024.0f, 0.0f), 1023.0f);
		return (leftShift3f_32b(uz) << 2) |
			(leftShift3f_32b(uy) << 1) |
			leftShift3f_32b(ux);
	}

	inline __host__ __device__ unsigned long long morton3f_64b(double x, double y, double z) {
		unsigned long long ux = fmin(fmax(x * 2097152.0f, 0.0f), 2097151.0f);
		unsigned long long uy = fmin(fmax(y * 2097152.0f, 0.0f), 2097151.0f);
		unsigned long long uz = fmin(fmax(z * 2097152.0f, 0.0f), 2097151.0f);
		return (leftShift3f_64b(z) << 2) |
			(leftShift3f_64b(y) << 1) |
			leftShift3f_64b(x);
	}

	inline __host__ __device__ unsigned int leftShift2f_32b(unsigned int x) {
		if (x == (1 << 16)) --x;
		x = (x | (x << 8)) & 0b00000000111111110000000011111111;
		x = (x | (x << 4)) & 0b00001111000011110000111100001111;
		x = (x | (x << 2)) & 0b00110011001100110011001100110011;
		x = (x | (x << 1)) & 0b01010101010101010101010101010101;
		return x;
	}

	inline __host__ __device__ unsigned int morton2f_32b(float x, float y) {
		unsigned int ux = fminf(fmaxf(x * 65536.0f, 0.0f), 65535.0f);
		unsigned int uy = fminf(fmaxf(y * 65536.0f, 0.0f), 65535.0f);
		return 
			(leftShift2f_32b(uy) << 1) |
			leftShift2f_32b(ux);
	}

}